#!/usr/bin/env bash
# vim:ts=4:sw=4:expandtab

##################################################################
# NAME
#     mkrun - a toy for run target's command
#
# SYNOPSIS
#     mkrun
#     mkrun [ options ] command-type targets ...
# 
#     debug=on mkrun ...
#
# OPTIONS
#       -h            Print this message and exit.
#
#       -m MODULENAME Use MODULENAME as module-name for each target.
#       -d DEPLOYPATH Use DEPLOYPATH as deploy-path for each target.
#
# AUTHORS
#     neiku project <ku7d@qq.com> 
#
# SEE ALSO
#     mkxrc_modules
#     mkxrc_targets
#     mkxrc_commands
#     mkxrc_targetregs
#     mkxrc_commandtpls
#
# VERSION
#     2015/11/21: 支持每个target指定类型的命令(start/stop/restart)
#     2015/11/25: 支持可配置登录方式(目前只支持rsa)
#                 支持pre-deploy/post-deploy类型命令
#                 支持命令别名(alias)
#     2015/11/28: 支持使用mkm查找target(project/global/system级别)
#                 支持使用mkm查找module(project/global/system级别)
#                 支持使用mkm查找command(project/global/system级别)
#     2015/12/03: 支持本地部署模块(local module)
#     2015/12/05: 支持基于密码登录的远程部署模块(passwd module)
#     2015/12/11: 支持自定义命令类型
#     2015/12/12: 支持命令模板(可配置不同模块的不同命令模板)
#     2015/12/23: 支持模式匹配target
#     2016/03/18: 支持通过环境变量与用户定义命令(模板)通信
#                 (废弃基于sed的变量替换命令生成机制)
#     2016/04/23: 支持命令行指定module-name(-m)、deploy-path(-d)
#                 (方便临时切换部署环境)
#                 MKX_MODULE   改名为 MKX_MODULENAME (模块名字)
#                 MKX_HOSTPATH 改名为 MKX_DEPLOYPATH (部署路径)
#     2016/05/01: 支持透传-m/-d选项给mkrun(处理alias命令)
#                 支持自定义module(module type = custom)
#                 (custom-runner 配置自定义命令执行工具)
#     2016/06/18: 支持su module(module type = su)
#     2017/01/16: 支持跨平台
#                 - 通过env程序+PATH变量动态查询bash，控制权
#                   、准确性都得到保障
#
##################################################################

# target(*) <-----> module(1)     <-----> destination(1)
# target(1) <-----> command(*)

# help
function help()
{
    echo "Usage: mkrun [ options ] command-type targets ..."
    echo "Options:"
    echo "  -h            Print this message and exit."
    echo ""
    echo "  -m MODULENAME Use MODULENAME as module-name for each target."
    echo "  -d DEPLOYPATH Use DEPLOYPATH as deploy-path for each target."
    echo ""
    echo "Report bugs to <ku7d@qq.com>"
}

# mkrun [ options ] command-type targets ...
cmdline_modulename=""
cmdline_deploypath=""

# parse cmdline
cmdline="$@"
mklog debug "origin-args:[$@]"
temp=$(getopt -o "hm:d:" --long "" -n "mkrun" -- "$@")
if [ $? != 0 ] ; then
    echo "`help`" >&2
    exit 1
fi
eval set -- "$temp"
mklog debug "parsed-args:[$temp]"
while true
do
    case "$1" in
        -h) echo "`help`" >&2; exit 0;;
        -m) cmdline_modulename="$2"; shift 2;;
        -d) cmdline_deploypath="$2"; shift 2;;
        --) shift ; break ;;
        *)  echo "parse options error!" >&2 ; exit 1 ;;
    esac
done
mklog debug "cmdline_modulename:[$cmdline_modulename], cmdline_deploypath:[$cmdline_deploypath]"

if [ $# -lt 2 ] ; then
    echo "`help`" >&2;
    exit 1
fi

# get command-type
cmdtype="$1"
shift 1

# run target's command with type
for target in $*
do
    # .so target is specail
    if expr match "$target" ".*\.so$" >/dev/null 2>&1 ; then
        if ! expr match "$target" "^lib" >/dev/null 2>&1 ; then
            target="lib$target"
        fi
    fi
    # .a target is specail
    if expr match "$target" ".*\.a$" >/dev/null 2>&1 ; then
        if ! expr match "$target" "^lib" >/dev/null 2>&1 ; then
            target="lib$target"
        fi
    fi

    # find target's module
    # or use user-defined module-name/deploy-path in cmdline
    modulename="$cmdline_modulename";
    deploypath="$cmdline_deploypath";
    if [ -z "$modulename" -o -z "$deploypath" ] ; then
        # find modulename or deploypath by target
        eval `mkm find target $target \
              | awk '{printf "modulename=%s; deploypath=%s;" \
                             ,           $1,            $2}'`
        if [ -z "$modulename" -o -z "$deploypath" ] ; then
            eval `mkm find targetreg $target \
                 | awk '{printf "modulename=%s; deploypath=%s;" \
                                ,           $1,            $2}'`
            if [ -z "$modulename" -o -z "$deploypath" ] ; then
                mklog error "module not found, target:[$target]"
                continue
            fi
            mklog debug "module found in targetreg, target:[$target]"
        fi 

        # use user-defined module name (from cmdline)
        if [ -n "$cmdline_modulename" ] ; then
            modulename="$cmdline_modulename"
        fi

        # use user-defined deploy path (from cmdline)
        if [ -n "$cmdline_deploypath" ] ; then
            deploypath="$cmdline_deploypath"
        fi
    fi
    mklog debug "module:[$modulename], deploypath:[$deploypath], target:[$target]"

    # load target's command
    command=""
    eval `mkm find command $target $cmdtype | while read target type command
          do
              echo "command='$command';"
          done` >/dev/null 2>&1
    if [ $? -ne 0 ] ; then
        mklog error "load '$cmdtype' command fail, target:[$target]"
        continue
    fi 
    mklog debug "target:[$target], cmdtype:[$cmdtype], command:[$command]"
    if [ -z "$command" ] ; then
        # no command yet, try command tpl
        cmdtpl=""
        eval `mkm find commandtpl $modulename $cmdtype | while read moduledump typedump commandtpl
              do
                  echo "cmdtpl='$commandtpl';"
              done` >/dev/null 2>&1
        mklog debug "module:[$modulename], target:[$target], cmdtype:[$cmdtype], cmdtpl:[$cmdtpl]"
        if [ -z "$cmdtpl" ] ; then
            mklog error "'$cmdtype' command and default '$cmdtype' command tpl not found," \
                        "module:[$modulename], target:[$target], cmdtype:[$cmdtype]"
            continue
        fi
        mklog debug "module:[$modulename], target:[$target], command tpl:[$cmdtpl]"

        command="$cmdtpl"
    fi

    # maybe command alias
    if [ "${command:0:1}" = "@" ] ; then
        mklog debug "$target's '$cmdtype' command is alias '${command:1}' command"
        mkrun -m"$modulename" -d"$deploypath" "${command:1}" $target
        continue
    fi

    # load module's info
    mtype=""; username=""; rsapkey=""; hostname=""; hostport="";
    eval `mkm find module $modulename \
          | awk '{printf "mtype=%s; username=%s; rsapkey=%s; hostname=%s; hostport=%s;" \
                         ,      $2,          $3,         $4,          $5,          $6}'`
    mklog debug "target:[$target], module:[$modulename], mtype:[$mtype]," \
                "username:[$username], rsa-private:[$rsapkey]," \
                "hostname:[$hostname], hostport:[$hostport]"
    if [   "$mtype" != "rsa"    \
        -a "$mtype" != "local"  \
        -a "$mtype" != "passwd" \
        -a "$mtype" != "custom" \
        -a "$mtype" != "su" ] ; then
        mklog error "module type unsupported, type:[$mtype]," \
                    "supported type:[rsa, local, passwd, custom, su], target:[$target]"
        continue
    fi

    # export env before running command
    exportcmd="export"
    exportcmd="$exportcmd MKX_MODULENAME='$modulename'"
    exportcmd="$exportcmd MKX_TARGET='$target'"
    exportcmd="$exportcmd MKX_MTYPE='$mtype'"
    exportcmd="$exportcmd MKX_CMDTYPE='$cmdtype'"
    exportcmd="$exportcmd MKX_DEPLOYPATH='$deploypath'"

    # run command
    mklog normal "run '$cmdtype' command for '$target' with command:[$command]"
    case "$mtype" in
        rsa)
            if [   -z "$username" -o -z "$rsapkey"  \
                -o -z "$hostname" -o -z "$hostport" ]
            then
                mklog error "rsa module not ok, target:[$target], module:[$modulename]"
                continue
            fi
            ssh -i $rsapkey -p $hostport $username@$hostname "$exportcmd; $command"
            ;;
        local)
            eval "$exportcmd; $command"
            ;;
        passwd)
            if [   -z "$username" -o -z "$rsapkey"  \
                -o -z "$hostname" -o -z "$hostport" ]
            then
                mklog error "passwd module not ok, target:[$target], module:[$modulename]"
                exit 1
            fi
            mkssh $username $rsapkey $hostname $hostport "$exportcmd; $command"
            ;;
        custom)
            # custom runner
            customrunner="`mkm get config custom-runner`"
            if [ -z "$customrunner" ] ; then
                mklog error "custom runner not found, target:[$target], module:[$modulename]"
                continue
            fi
            mklog debug "custom-runner:[$customrunner]"

            # run runner
            export MKX_CUSTOM_TARGETPATH="$makedir/$target" \
                   MKX_CUSTOM_TARGETNAME="$target"          \
                   MKX_CUSTOM_DEPLOYPATH="$deploypath/"     \
                   MKX_CUSTOM_CONFIG1="$modulename"         \
                   MKX_CUSTOM_CONFIG2="$mtype"              \
                   MKX_CUSTOM_CONFIG3="$username"           \
                   MKX_CUSTOM_CONFIG4="$rsapkey"            \
                   MKX_CUSTOM_CONFIG5="$hostname"           \
                   MKX_CUSTOM_CONFIG6="$hostport"           \
                   MKX_CUSTOM_COMMAND="$exportcmd; $command"
            eval "$customrunner"
            unset  MKX_CUSTOM_TARGETPATH \
                   MKX_CUSTOM_TARGETNAME \
                   MKX_CUSTOM_DEPLOYPATH \
                   MKX_CUSTOM_CONFIG1    \
                   MKX_CUSTOM_CONFIG2    \
                   MKX_CUSTOM_CONFIG3    \
                   MKX_CUSTOM_CONFIG4    \
                   MKX_CUSTOM_CONFIG5    \
                   MKX_CUSTOM_CONFIG6    \
                   MKX_CUSTOM_COMMAND
            ;;
        su)
           MKX_MKSU_PASSWORD="$rsapkey" mksu $username "" "$exportcmd; $command"
            ;;
        *)
            mklog error "module type unsupported, type:[$mtype]," \
                        "supported type:[rsa, local, passwd, custom, su]"
            exit 1
            ;;
    esac
done
